package org.class2table.core.clazz;

import cn.hutool.core.util.StrUtil;
import org.class2table.core.Class2TableException;
import org.class2table.core.parent.ParentProcessor;
import org.class2table.core.parent.bean.PrimaryKeyInfo;
import org.class2table.core.clazz.annotations.Column;
import org.class2table.core.clazz.annotations.PrimaryKey;
import org.class2table.core.clazz.annotations.Table;
import org.class2table.core.clazz.bean.ClazzMetaData;
import org.class2table.core.clazz.bean.PropertyMetaData;
import org.class2table.core.dictionary.Constants;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class ClazzProcessor {
    public static ClazzMetaData getClassMetaData(Class<?> clazz, Map<Class<?>, String> clazz2ColumnTypeRuleMap) {
        ClazzMetaData clazzMetaData = new ClazzMetaData();
        List<PropertyMetaData> propertyMetaDataList = new ArrayList<>();
        Table tableAnnotation = clazz.getAnnotation(Table.class);
        String tableName = tableAnnotation.tableName();
        clazzMetaData.setTableName(tableName);
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            String fieldName = declaredField.getName();
            Class<?> type = declaredField.getType();
            String columnName;
            String columnDefault;
            String columnType;
            boolean isNullable;
            boolean isSetDefault;
            String columnKey = "";
            Integer sort = null;
            String columnComment;

            Column columnAnnotation = declaredField.getAnnotation(Column.class);
            if (Objects.nonNull(columnAnnotation)) {
                columnName = StrUtil.isEmpty(columnAnnotation.columnName()) ? StrUtil.toUnderlineCase(fieldName) : columnAnnotation.columnName();
                columnDefault = columnAnnotation.columnDefault();
                columnType = columnAnnotation.columnType();
                columnComment = columnAnnotation.columnComment();
                if (StrUtil.isEmpty(columnType)) {
                    columnType = clazz2ColumnTypeRuleMap.get(type);
                    if (Objects.isNull(columnType)) {
                        throw new Class2TableException(String.format("unknown class type [%s] of [%s.%s]! please use'@Column' to set columnType，or add the class type in clazz2ColumnTypeRuleMap!",type.getName(), clazz.getName(), fieldName));
                    }
                }
                isNullable = columnAnnotation.isNullable();
                isSetDefault = columnAnnotation.isSetDefault();
            } else {
                columnName = StrUtil.toUnderlineCase(fieldName);
                columnDefault = Constants.COMMON.blankStr;
                columnComment = Constants.COMMON.blankStr;
                columnType = clazz2ColumnTypeRuleMap.get(type);
                if (Objects.isNull(columnType)) {
                    throw new Class2TableException(String.format("unknown class type [%s] of [%s.%s]! please use'@Column' to set columnType，or add the class type in clazz2ColumnTypeRuleMap!",type.getName(), clazz.getName(), fieldName));
                }
                isNullable = true;
                isSetDefault = false;
            }
            PrimaryKey primaryKey = declaredField.getAnnotation(PrimaryKey.class);
            if (Objects.nonNull(primaryKey)) {
                isNullable = false;
                columnKey = Constants.INFORMATION_SCHEMA.COLUMN_KEY.PRI;
                sort = primaryKey.value();
            }
            PropertyMetaData propertyMetaData = new PropertyMetaData();
            propertyMetaData.setColumnName(columnName);
            propertyMetaData.setColumnDefault(columnDefault);
            propertyMetaData.setColumnType(columnType);
            propertyMetaData.setIsNullable(isNullable);
            propertyMetaData.setIsSetDefault(isSetDefault);
            propertyMetaData.setColumnKey(columnKey);
            propertyMetaData.setColumnKeySort(sort);
            propertyMetaData.setColumnComment(columnComment);
            propertyMetaDataList.add(propertyMetaData);
        }

        List<PrimaryKeyInfo> primaryKeyInfoList = ParentProcessor.extractPrimaryKeyInfos(propertyMetaDataList);

        clazzMetaData.setEntityAttrMetaDataList(propertyMetaDataList);
        clazzMetaData.setPrimaryKeyInfoList(primaryKeyInfoList);
        return clazzMetaData;
    }

}
